package ru.urfc.portal.managers.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;

import ru.urfc.portal.dao.BasePersistence;
import ru.urfc.portal.dao.TmpContragentDao;
import ru.urfc.portal.dao.filter.Filter;
import ru.urfc.portal.exceptions.UserException;
import ru.urfc.portal.managers.BaseManager;
import ru.urfc.portal.managers.ClientManager;
import ru.urfc.portal.model.AssociationKey;
import ru.urfc.portal.model.AssociationKeyWithEmail;
import ru.urfc.portal.model.Client;
import ru.urfc.portal.model.ClientUpdate;
import ru.urfc.portal.model.Branch;
import ru.urfc.portal.model.TmpContragent;
import ru.urfc.portal.model.accounting.Contragent;
import ru.urfc.portal.model.admins.User;
import ru.urfc.portal.model.admins.UserCreate;
import ru.urfc.portal.model.admins.UserRole;
import ru.urfc.portal.model.admins.UserSelfUpdate;
import ru.urfc.portal.model.admins.UserState;
import ru.urfc.portal.model.admins.UserUpdate;
import ru.urfc.portal.operations.ClientsSynchronizer;
import ru.urfc.portal.operations.ClientsSynchronizer.BranchContragents;
import ru.urfc.portal.ui.client.validators.EmailValidator;

@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class ClientManagerImpl extends BaseManager implements ClientManager {

	//@EJB ClientsSynchronizer synchronizer;
	
	@EJB BasePersistence basePersistence;
	@EJB TmpContragentDao tmpContragentDao;
	
	@Override
	public Client getClient(Long clientId) {
		return super.getClient(clientId);
	}

	@Override
	public List<Client> getClients(Filter branchFilter, Filter spectrFilter) {
		int maxResults = 100;
		List<BranchContragents> branchesContragents = new ArrayList<BranchContragents>();
		for (Branch branch: find(Branch.class, branchFilter)) {
			Filter filter = new Filter(spectrFilter);
			filter.setPaging(0, maxResults);
			List<Contragent> contragents = find(branch, Contragent.class, filter);
			maxResults -= contragents.size();
			branchesContragents.add(new BranchContragents(branch, contragents));
		}
		List<TmpContragent> tmp = new ClientsSynchronizer(basePersistence, tmpContragentDao).synchronize(branchesContragents);
		return asClients(tmp);
	}
	

	private List<Client> asClients(List<TmpContragent> contragents) {
		ArrayList<Client> result = new ArrayList<Client>();
		for (TmpContragent tmp: contragents) {
			Client client = tmp.getClient();
			client.setContragent(new Contragent(tmp));
			result.add(client);
		}
		return result;
	}

	@Override
	public Contragent getContragent(Long clientId) {
		return findById(Contragent.class, getContragentId(clientId));
	}
	
	@Override
	public AssociationKey generateKey(Long clientId) {
		AssociationKey oldKey = oneOrNull(find(AssociationKey.class, "client.id", clientId));
		if (oldKey != null) {
			delete(oldKey);
		}
		Client client = findById(Client.class, clientId);
		return persist(new AssociationKey(UUID.randomUUID().toString(), client));
	}

	@Override
	public Client updateClient(Long clientId, ClientUpdate clientUpdate) {
		Client client = getClient(clientId);
		client.update(clientUpdate);
		return client;
	}

	@Override
	public User getUser(Long clientId, Long userId) {
		User user = findById(User.class, userId);
		return user.check(clientId);		
	}
	
	@Override
	public List<User> getUsers(Long clientId) {
		return find(User.class, "client.id", clientId);
	}
	
	@Override
	public User createUser(Long clientId, UserCreate userInfo) {
		if (null != EmailValidator.INSTANCE.validate(userInfo.getEmail())) {
			throw new UserException("Ошибка в email");
		}
		Client client = getClient(clientId);
		User user = new User(client, userInfo);
		user.setRole(exactlyOne(find(UserRole.class, "code", CODE_USER_ROLE_USER)));
		user.setState(exactlyOne(find(UserState.class, "code", CODE_USER_STATE_INVITED)));
		return persist(user);
	}

	@Override
	public User updateUser(Long clientId, Long userId, UserUpdate userUpdate) {
		User user = getUser(clientId, userId);
		user.setRole(exactlyOne(find(UserRole.class, "code", userUpdate.getRole().getCode())));
		user.setState(exactlyOne(find(UserState.class, "code", userUpdate.getState().getCode())));
		return user;
	}
	
	@Override
	public void deleteUser(Long clientId, Long userId) {
		delete(getUser(clientId, userId));
	}

	@Override
	public User getUserByEmailAndActivate(String email) {
		if (email == null) {
			return null;
		}
		final User result = exactlyOne(find(User.class, "email", email));
		result.getClient();// fetching
		if (result.getState().getCode().equals(CODE_USER_STATE_INVITED)) {
			result.setState(exactlyOne(find(UserState.class, "code", CODE_USER_STATE_ACTIVE)));
		}
		return result;
	}

	@Override
	public User getUserForHimSelf(Long userId) {
		return findById(User.class, userId);
	}
	
	@Override
	public User updateUser(Long userId, UserSelfUpdate userSelfUpdate) {
		return getUserForHimSelf(userId).update(userSelfUpdate);
	}

	@Override
	public boolean associate(AssociationKeyWithEmail associationKey) {
		AssociationKey key = oneOrNull(find(AssociationKey.class, "hash", associationKey.evaluateHash()));
		if (key == null) {
			return false;
		}
		User user = oneOrNull(find(User.class, "email", associationKey.getEmail()));
		if (user == null) {
			user = persist(new User(associationKey.getEmail()));
		}
		user.setRole(exactlyOne(find(UserRole.class, "code", CODE_USER_ROLE_ADMIN)));
		user.setState(exactlyOne(find(UserState.class, "code", CODE_USER_STATE_ACTIVE)));
		user.setClient(key.getClient());
		key.getClient().setAdminRegistered(true);
		delete(key);
		return true;
	}

}
